The Python to IDL bridge provides a mechanism for calling IDL code from Python. The IDL module contains a set of methods that allow you to initialize and interact with the IDL interpreter. In addition, each object passed back from IDL is wrapped in an instance of the IDL class; you can then call methods on the underlying IDL object, or set and retrieve attributes on the object.
Note: See Python Bridge for installation instructions.
Call an IDL function or method directly from Python code, as if it was a Python object:
>>> from idlpy import IDL
>>> arr = IDL.findgen(100)/50*3.14159
>>> x = 50*IDL.sin(arr)
>>> y = 50*IDL.cos(arr)
>>> m = IDL.map(test=1)
% Compiled module: MAP.
>>> p = IDL.plot(x - 90, y, 'r-o2', overplot=1)
% Compiled module: PLOT.
>>> p = IDL.plot(x + 90, y, 'b-o2', overplot=1)
>>> m.save('map.png', resolution=96, border=0, transparent=1)
Notice that any output from IDL gets printed out in the Python console.
Execute the same commands, but called directly within the IDL interpreter:
>>> IDL.run("arr = findgen(100)/50*3.14159")
>>> IDL.run("help, arr", stdout=1)
ARR FLOAT = Array[100]
>>> IDL.run("x = 50*sin(arr)")
>>> IDL.run("y = 50*cos(arr)")
>>> IDL.run("m = map(/test)")
>>> IDL.run("p = plot(x - 90, y, 'r-o2', /overplot)")
>>> IDL.run("p = plot(x + 90, y, 'b-o2', /overplot)")
>>> IDL.run("m.save, 'map.png', resolution=96, border=0, /transparent")
To start using the IDL module from within Python, you can use the following command:
>>> from idlpy import IDL
At this point you now have access to all of the methods on the IDL class.
The IDL.run method executes IDL commands within the IDL interpreter. Any output produced by IDL is either returned in the result or redirected to the Python console.
Execute some arbitrary IDL code and print the result:
>>> IDL.run('arr = FINDGEN(100)')
>>> IDL.run('help, arr', stdout=1)
ARR FLOAT = Array[100]
Execute multiple IDL statements using the "&" operator:
>>> result = IDL.run('for i=0,9 do begin & print, i & endfor')
>>> print(result)
0
1
...
9
Result = IDL.run( command [, stdout=False] [, silent=False] )
By default the return value is a Python string containing all of the IDL output. If stdout is set to True then all of IDL's output is redirected to the Python console and the return value is None.
A string containing the IDL statements to execute in the IDL interpreter. You can send a multi-statement command by using a single string and separating the statements with the IDL "&" operator.
Set this keyword to True to redirect all of IDL's output to the Python console.
Set this keyword to True to avoid saving the command to IDL's command history.
Once you have imported the IDL module, you can call any function or procedure on IDL's path using the "dot" notation. Arguments and keywords can be passed to the routine. For example:
>>> from idlpy import IDL
>>> arr = IDL.randomu(None, 10000)
>>> arr = IDL.smooth(arr, 50)
>>> spec = IDL.fft_powerspectrum(arr, 0.1)
% Compiled module: FFT_POWERSPECTRUM.
>>> import matplotlib.pyplot as plt
>>> plt.semilogy(spec)
>>> plt.show()
This creates the figure shown to the right.
If you have retrieved an object from IDL, you can call methods or get and set properties on that object by using the "dot" notation. For example, we could construct an IDL graphics plot, modify some properties on the plot, and then save the plot to a file:
>>> from idlpy import IDL
>>> import numpy.random as ran
>>> arr = ran.rand(100)
>>> p = IDL.plot(arr, title='My Plot')
>>> p.color = 'red'
>>> p.save('myplot.pdf')
>>> p.close()
IDL variable and method names are case insensitive while Python names are case sensitive. When you call an IDL function or method, such as the save method on the IDL PLOT class, the bridge will automatically convert the name to uppercase before asking IDL for the routine.
Normally, you will pass Python variables into IDL function calls as input arguments or keywords, and receive the result back as an Python variable. However, if you use the IDL.run method, you may want to pass variables to or from IDL directly. To do this, you can use the standard Python attribute notation on the IDL class. For example:
>>> from idlpy import IDL
>>> import numpy.random as ran
>>> arr = ran.rand(5)
>>> IDL.arr = arr # pass by reference
>>> IDL.run('print, arr')
0.64949746 0.42264582 0.54525948 0.54869483 0.97396999
>>> arr[0] = 99
>>> IDL.run('print, arr')
99.000000 0.42264582 0.54525948 0.54869483 0.97396999
>>> from idlpy import IDL
>>> IDL.run('arr2 = FINDGEN(10,20,30)')
>>> arr2 = IDL.arr2 # pass by value
>>> arr2.shape
(30, 20, 10)
Note: All variables that are passed from IDL back to Python will be given a lowercase name.
To retrieve an IDL system variable such as !DPI or !CONST, you can use the Python getattr() method:
>>> from idlpy import IDL
>>> getattr(IDL, "!dpi")
3.1415926535897931
>>> const = getattr(IDL, "!const")
>>> const.keys()
dict_keys(['ME', 'PARSEC', 'DTOR', 'RYDBERG', 'MP', 'F', 'NA', 'M_EARTH', 'RE', 'P0', 'MN', 'G', 'H', 'PI', 'N0', 'T0', 'K', 'MU0', 'R_EARTH', 'VM', 'R', 'EPS0', 'LY', 'E', 'PHI', 'RTOD', 'ALPHA', 'U', 'AU', 'EULER', 'HBAR', 'I', 'GN', 'SIGMA', 'C', 'M_SUN'])
>>> const['PARSEC']
30856775814671912.0
__main__ variables to IDL. Changing one of the elements in either IDL or Python will change the corresponding element on the other side.__main__ level. In other words, a copy of the entire array is made. This is because the IDL code could delete the variable, but we have no way of knowing if the variable is still in use on the Python side since Python has no simple way to notify when all references to a variable are gone.When variables are passed from IDL to Python or Python to IDL (as arguments, keywords, or __main__ variables), the following datatype conversions take place in both directions:
|
IDL Datatype |
Python Type |
|---|---|
|
!NULL (0) |
None |
|
Byte (1) |
numpy.uint8 |
|
Int (2) |
numpy.int16 |
|
Long (3) |
numpy.int32 |
|
Float (4) |
numpy.float32 |
|
Double (5) |
numpy.float64, float |
|
Complex (6) |
numpy.complex64 |
|
String (7) |
str, unicode |
|
Structure (8) |
see tables below |
|
Double Complex (9) |
numpy.complex128 |
|
Pointer (10) |
not allowed |
|
Objref (11) |
see tables below |
|
Uint (12) |
numpy.uint16 |
|
Ulong (13) |
numpy.uint32 |
|
Long64 (14) |
numpy.int64 |
|
Ulong64 (15) |
numpy.uint64 |
The following datatypes are used when converting from IDL to Python:
|
IDL Datatype |
Python Type |
|---|---|
|
Structure (8) |
dict |
|
List object |
list |
|
Hash, Dictionary, OrderedHash object |
dict |
|
IDL PYTHON object |
corresponding Python object |
|
Other IDL class |
not allowed |
The following datatypes are used when converting from Python to IDL:
|
Python Type |
IDL Type |
|---|---|
|
bool, numpy.bool_ |
Byte (1), with boolean flag |
|
int, long |
Long (3) or Long64 (14), depending upon value |
|
float |
Double (5) |
|
complex |
Double Complex (9) |
|
bytes, bytearray |
String (7) |
|
unicode |
String (7) |
|
list |
List object |
|
tuple |
List object |
|
dict |
Hash object |
|
other Python object |
IDL PYTHON object that wraps the Python object |
|
Python IDL object |
corresponding IDL PYTHON object |
In IDL the data within a multi-dimensional array is organized as "column major," while in Python the data is organized as "row major."
In IDL, for a two-dimensional array the first dimension represents the "columns" of that array, while the second dimension represents the "rows". In terms of memory layout, the first dimension varies the "fastest", and has a stride equal to the size of the datatype in bytes. The second dimension varies the "slowest" and has a stride equal to the number of columns multiplied by the datatype size in bytes. For example, for a 16-bit integer array each column is separated by two bytes while each row is separated by 6 bytes:
IDL> x = INDGEN(3,2)+1 ; 3 columns by 2 rows
IDL> PRINT, x
1 2 3
4 5 6
IDL> PRINT, x[2,1] ; value in column 2, row 1
6
IDL> PRINT, BYTE(x, 0, 6*2) ; print the byte representation
1 0 2 0 3 0 4 0 5 0 6 0
In Python, for a two-dimensional array the first dimension represents the "rows" of that array, while the second dimension represents the "columns". In terms of memory layout, the first dimension varies the "slowest", and has a stride equal to the number of columns multiplied by the datatype size in bytes. The second dimension varies the "fastest" and has a stride equal to the size of the datatype in bytes. For example, for a 16-bit integer array each column is separated by two bytes while each row is separated by 6 bytes:
>>> import numpy as np
>>> x = np.array([[1, 2, 3], [4, 5, 6]], np.int16)
>>> x.shape
(2L, 3L)
>>> x.strides
(6L, 2L)
>>> x[1,2] # value in row 1, column 2
6
>>> x.tobytes()
'\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00'
Notice that in both Python and IDL the bytes are organized the same in memory — it is only the way that the dimensions are reported and the indexing order that is different.
When Python passes an array to IDL, or when IDL passes an array back to Python, the bytes within the data array remain unchanged. Instead, the order of the dimensions is reversed. For example, if we create an array in Python that has dimensions [3, 640, 480], then in IDL the dimensions will be reported as [480, 640, 3]:
>>> import numpy as np
>>> arr = np.ndarray((640, 480, 3), np.uint8)
>>> arr.shape
(640, 480, 3)
>>> IDL.arr = arr
>>> IDL.run('help, arr')
ARR BYTE = Array[3, 480, 640]
In most cases this will be the desired behavior. However, in some cases you may need to transpose your actual data values. For example, you might read in the data from a file in Python, and then run the data through an IDL or ENVI algorithm that expects a certain order. In IDL you can use the TRANSPOSE function to re-order the values within an array before sending it to Python, while in Python you can use numpy.transpose.
|
8.5 |
Introduced |